//
//  CoreDataExtension.swift
//  JKTools
//
//  Created by 姜奎 on 2/20/25.
//

import Foundation
import CoreData

    /// 数据模型装载数据
public protocol JKCoreDataModelProtocol {
    @discardableResult
    static func insertNewObject<T>(with properties: T, into context: NSManagedObjectContext) -> NSManagedObject?

    func insertNewSubObject(with entity: NSManagedObject)
}

//func convert() -> T
//
//func convertToDictionary() -> [String: Any]

struct JKCoreDataError: Error {
    let message: String
}


open class JKCoreDataProvider {
        /// 数据库模版文件
        /// - Returns: 文件名称，需要 重写
    open func momdName() -> String {
        fatalError("This method must be overridden,`\(type(of: self)) -momdName:`")
    }

        /// 数据库模版文件
        /// - Returns: 文件所在Bundle，需要 重写
    open func momdBundle() -> Bundle {
        fatalError("This method must be overridden,`\(type(of: self)) -momdBundle:`")
    }

        /// 数据库模版文件
        /// - Returns: 文件存储类型，默认：NSSQLiteStoreType
    open func persistentStoreType() -> String {
        return NSSQLiteStoreType
    }

        /// 数据库模版文件
        /// - Returns: 持久化存储的配置名称, 默认： nil
    open func persistentStoreConfigurationName() -> String? {
        return nil
    }

        /// 数据库模版文件
        /// - Returns: 持久化存储的地址，默认`documentDirectory + momdName() + .sqlite`
    open func persistentStoreURL() -> URL {
        let documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let storeURL = documentPath.appendingPathComponent("\(momdName()).sqlite")
        return storeURL
    }

        /// 数据库模版文件
        /// - Returns: 可以设置是否应该自动迁移数据模型，或者是否应该使用写时复制（Write-Ahead Logging，WAL）模式等,
        /// 默认：当模型存在变更，自动尝试适配新数据模型，并自动执行迁移
        /// NSMigratePersistentStoresAutomaticallyOption: true,
        /// NSInferMappingModelAutomaticallyOption: true
    open func persistentStoreOptions() -> [String: NSObject]? {
        return [NSInferMappingModelAutomaticallyOption: NSNumber(booleanLiteral: true)]
    }

        /// fetch筛选逻辑
        /// - Parameter context: moc
        /// - Returns: fetch筛选逻辑
    open func fetchRequest<T>() -> NSFetchRequest<T> {
        fatalError("This method must be overridden,`\(type(of: self)) -fetchRequest:`")
    }
}

open class JKCoreDataStorage {

    let _provider :JKCoreDataProvider

    private var persistentContainer: NSPersistentContainer!

    init(with provider :JKCoreDataProvider) throws {
        _provider = provider
        try initializeContainer()
        configureContainer()
        loadPersistentStores()
    }

    private func initializeContainer() throws {

        var momdPath = _provider.momdBundle().url(forResource: _provider.momdName(), withExtension: "momd")

        if momdPath == nil {
            momdPath = _provider.momdBundle().url(forResource: _provider.momdName(), withExtension: "mom")
        }

        guard let momdPath = momdPath else {
            throw JKCoreDataError(message: "NSPersistentContainer init \(_provider.momdName()) error")
        }
        guard let entityModel = NSManagedObjectModel(contentsOf: momdPath) else {
            throw JKCoreDataError(message: "NSPersistentContainer init \(momdPath.absoluteString) error")
        }

        persistentContainer = NSPersistentContainer(name: _provider.momdName(), managedObjectModel: entityModel)
    }

        /// 容器配置
    private func configureContainer() {
        let description = NSPersistentStoreDescription(url: _provider.persistentStoreURL())

        description.configuration = _provider.persistentStoreConfigurationName()

        if let persistentStoreOptions = _provider.persistentStoreOptions() {
            for (key, value) in persistentStoreOptions {
                description.setOption(value, forKey: key)
            }
        }

        description.type = _provider.persistentStoreType()

        description.shouldMigrateStoreAutomatically = true

        persistentContainer.persistentStoreDescriptions = [description]
    }

    private func loadPersistentStores() {
        persistentContainer.loadPersistentStores { [weak self] storeDescription, error in
            guard let self = self,
                  let error = error as? NSError else {
                    // 成功加载后配置

                self?.mainPerform { moc in
                    moc.automaticallyMergesChangesFromParent = true
                    moc.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
                }
                return
            }

            print("⚠️ 首次加载失败: $error.domain) Code $error.code)")

            self.handleStoreLoadingError(error, description: storeDescription)
        }
    }

        // MARK: - 错误处理
    private func handleStoreLoadingError(_ error: NSError, description: NSPersistentStoreDescription) {
        switch (error.domain, error.code) {
            case (NSCocoaErrorDomain, 134100): // 模型不兼容
                attemptMigration(for: description)
            case (NSCocoaErrorDomain, 134110): // 存储文件损坏
                removeCorruptedStore(at: description.url!) {
                    self.retryLoading()
                }
            case (NSPOSIXErrorDomain, 2): // 文件不存在
                recreateStoreFile(at: description.url!) {
                    self.retryLoading()
                }
            default:break
        }
    }

        // MARK: - 恢复操作
    private func attemptMigration(for description: NSPersistentStoreDescription) {
        print("⚠️ 尝试自动迁移...")
        description.shouldMigrateStoreAutomatically = true
        retryLoading()
    }

    private func removeCorruptedStore(at url: URL, completion: @escaping () -> Void) {
        print("⚠️ 删除损坏的存储文件: $url.lastPathComponent)")

        let coordinator = persistentContainer.persistentStoreCoordinator
        guard let store = coordinator.persistentStore(for: url) else {
            completion()
            return
        }

        do {
            try coordinator.remove(store)
        } catch {
            print("🗑️ 文件删除失败: \(error)")
        }
        do {
            try FileManager.default.removeItem(at: url)
        } catch {
            print("🗑️ 文件删除失败: \(error)")
        }
        do {
            try FileManager.default.removeItem(atPath: url.path.appending("-wal"))
        } catch {
            print("🗑️ 文件删除失败: \(error)")
        }
        do {
            try FileManager.default.removeItem(atPath: url.path.appending("-shm"))
        } catch {
            print("🗑️ 文件删除失败: \(error)")
        }

        completion()
    }

    private func recreateStoreFile(at url: URL, completion: @escaping () -> Void) {
        let directory = url.deletingLastPathComponent()

        do {
            try FileManager.default.createDirectory(at: directory,
                                                    withIntermediateDirectories: true)
            FileManager.default.createFile(atPath: url.path,
                                           contents: nil)
        } catch {
            print("📁 目录创建失败: $error)")
        }

        completion()
    }

        // MARK: - 重试机制
    private func retryLoading() {
        print("🔄 第 $currentRetry) 次重试加载...")
        persistentContainer.loadPersistentStores { _, error in
            if let error = error {
                print("⚠️ 重试失败: \(error)")
            } else {
                print("✅ 存储加载成功")
            }
        }
    }

    private func perform(_ block: @escaping (NSManagedObjectContext)-> Void) {
        if Thread.isMainThread {
            block(persistentContainer.viewContext)
        } else {
            persistentContainer.performBackgroundTask(block)
        }
    }

    private func mainPerform(_ block: @escaping (NSManagedObjectContext)-> Void) {
        if Thread.isMainThread {
            block(persistentContainer.viewContext)
        } else {
            DispatchQueue.main.async {
                block(self.persistentContainer.viewContext)
            }
        }
    }

    func insert<T>(_ entityType: any (JKCoreDataModelProtocol).Type, datas:[T], parent entityId: NSManagedObjectID? = nil, completion:@escaping (JKCoreDataError?)->Void) {

        persistentContainer.performBackgroundTask { moc in
            for data in datas {
                if let subEntity = entityType.insertNewObject(with: data, into: moc), let entityId = entityId, let parent = moc.object(with: entityId) as? JKCoreDataModelProtocol {
                    parent.insertNewSubObject(with: subEntity)
                }
            }
            do {
                try moc.save()
                completion(nil)
            } catch {
                let error = JKCoreDataError(message: error.localizedDescription)
                completion(error)
            }
        }
    }

    func delete(_ objectIDs: [NSManagedObjectID], completion:@escaping (JKCoreDataError?)->Void) {

        persistentContainer.performBackgroundTask { moc in
            for objectID in objectIDs {
                let obj = moc.object(with: objectID)
                moc.delete(obj)
            }
            do {
                try moc.save()
                completion(nil)
            } catch {
                let error = JKCoreDataError(message: error.localizedDescription)
                completion(error)
            }
        }
    }

    func deleteAll(_ completion: @escaping ()->Void) {
        persistentContainer.performBackgroundTask { [weak self] _ in
            guard let self = self else { return completion() }
            self.removeCorruptedStore(at: self._provider.persistentStoreURL()) {
                self.configureContainer()
                self.loadPersistentStores()
                completion()
            }
        }
    }

    func fetchObjects<T>(fetchRequest: NSFetchRequest<T>? = nil, completion:@escaping ([T],JKCoreDataError?)->Void) {
        let request = fetchRequest ?? self._provider.fetchRequest()
        self.perform { moc in
            do {
                let asyncFetch = NSAsynchronousFetchRequest(fetchRequest: request, completionBlock: { result in
                    completion(result.finalResult ?? [], nil)
                })
                try moc.execute(asyncFetch)
            } catch {
                let error = JKCoreDataError(message: error.localizedDescription)
                completion([], error)
            }
        }
    }

        /// fetch筛选控制器
        /// - Parameter context: moc
        /// - Returns: fetch筛选控制器
    @MainActor
    open func fetchedResultController<T>(fetchRequest: NSFetchRequest<T>? = nil, sectionNameKeyPath:String? = nil) throws -> NSFetchedResultsController<T> {
        guard Thread.isMainThread else {
            throw JKCoreDataError(message: "This method must be overridden,`\(type(of: self)) -fetchedResultController:`")
        }

        let request = fetchRequest ?? self._provider.fetchRequest()

        return NSFetchedResultsController(fetchRequest: request, managedObjectContext: persistentContainer.viewContext, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
    }

    open func fetchedResultController<T>(fetchRequest: NSFetchRequest<T>? = nil, sectionNameKeyPath:String? = nil, completion: @escaping (NSFetchedResultsController<T>)->Void) {

        let request = fetchRequest ?? self._provider.fetchRequest()

        mainPerform { context in
            completion(NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil))
        }
    }
}

class JKCoreDataStack {

    private let storage: JKCoreDataStorage

    init(_ provider:JKCoreDataProvider) throws {
        self.storage = try JKCoreDataStorage(with: provider)
    }

    func insert<T>(_ entityType: any (JKCoreDataModelProtocol).Type, datas:[T], parent entityId: NSManagedObjectID? = nil, completion:@escaping (JKCoreDataError?)->Void) {
        storage.insert(entityType, datas: datas,parent: entityId, completion: completion)
    }

    func delete(_ objectIDs: [NSManagedObjectID], completion:@escaping (JKCoreDataError?)->Void) {

        storage.delete(objectIDs, completion: completion)
    }

    func deleteAll(_ completion: @escaping ()->Void) {
        storage.deleteAll(completion)
    }

    func fetchObjects<T>(fetchRequest: NSFetchRequest<T>? = nil, completion:@escaping ([T],JKCoreDataError?)->Void) where T: NSFetchRequestResult {
        storage.fetchObjects(fetchRequest: fetchRequest, completion: completion)
    }

        /// fetch筛选控制器
        /// - Parameter context: moc
        /// - Returns: fetch筛选控制器
    @MainActor
    open func fetchedResultController<T>(fetchRequest: NSFetchRequest<T>? = nil, sectionNameKeyPath:String? = nil) throws -> NSFetchedResultsController<T> {
        return try storage.fetchedResultController(fetchRequest: fetchRequest, sectionNameKeyPath: sectionNameKeyPath)
    }

    open func fetchedResultController<T>(fetchRequest: NSFetchRequest<T>? = nil, sectionNameKeyPath:String? = nil, completion: @escaping (NSFetchedResultsController<T>)->Void) {
        storage.fetchedResultController(fetchRequest: fetchRequest, sectionNameKeyPath: sectionNameKeyPath,completion: completion)
    }
}
